package net.lab1024.sa.admin.module.business.roland.service;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.admin.module.business.roland.dao.RolandCustomerDao;
import net.lab1024.sa.admin.module.business.roland.dao.RolandOrderDao;
import net.lab1024.sa.admin.module.business.roland.dao.RolandPerformanceStudentDao;
import net.lab1024.sa.admin.module.business.roland.dao.RolandSeaDao;
import net.lab1024.sa.admin.module.business.roland.domain.dto.TicketGenerateDTO;
import net.lab1024.sa.admin.module.business.roland.domain.entity.RolandCustomerEntity;
import net.lab1024.sa.admin.module.business.roland.domain.entity.RolandOrderEntity;
import net.lab1024.sa.admin.module.business.roland.domain.entity.RolandPerformanceStudentEntity;
import net.lab1024.sa.admin.module.business.roland.domain.entity.RolandSeaEntity;
import net.lab1024.sa.admin.module.business.roland.domain.form.RolandOrderForm;
import net.lab1024.sa.admin.module.business.roland.domain.form.RolandOrderQueryForm;
import net.lab1024.sa.admin.module.business.roland.domain.form.RolandOrderUpdateForm;
import net.lab1024.sa.admin.module.business.roland.domain.form.RolandTicketQueryForm;
import net.lab1024.sa.admin.module.business.roland.domain.vo.RolandMyTicketVO;
import net.lab1024.sa.admin.module.business.roland.domain.vo.RolandOrderDetailVO;
import net.lab1024.sa.admin.module.business.roland.domain.vo.RolandOrderExcelVO;
import net.lab1024.sa.admin.module.business.roland.domain.vo.RolandOrderVO;
import net.lab1024.sa.common.common.code.UserErrorCode;
import net.lab1024.sa.common.common.domain.PageResult;
import net.lab1024.sa.common.common.domain.ResponseDTO;
import net.lab1024.sa.common.common.util.SmartEasyPoiExcelUtil;
import net.lab1024.sa.common.common.util.SmartPageUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 客户订单表 Service
 *
 * @Author dragon
 * @Date 2023-12-11 13:27:12
 * @Copyright dragon
 */
@Slf4j
@Service
public class RolandOrderService {

    @Autowired
    private RolandOrderDao rolandOrderDao;
    @Autowired
    private RolandCustomerDao rolandCustomerDao;
    @Autowired
    private RolandPerformanceStudentDao rolandPerformanceStudentDao;
    @Autowired
    private RolandSeaDao rolandSeaDao;
    @Value("${ticket.path}")
    private String ticketPath;
    @Autowired
    private PlatformTransactionManager txManager;


    public static final LinkedBlockingQueue<TicketGenerateDTO> TICKET_QUEUE = new LinkedBlockingQueue<>();


    private final BigDecimal price = new BigDecimal(99);

    /**
     * 分页查询
     *
     * @param queryForm
     * @return
     */
    public PageResult<RolandOrderVO> queryPage(RolandOrderQueryForm queryForm) {
        Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
        List<RolandOrderVO> list = rolandOrderDao.queryPage(page, queryForm);
        PageResult<RolandOrderVO> pageResult = SmartPageUtil.convert2PageResult(page, list);
        return pageResult;
    }

    /**
     * 添加
     */
    public synchronized ResponseDTO<BigDecimal> order(RolandOrderForm addForm) {
        RolandCustomerEntity customer = rolandCustomerDao.selectById(addForm.getCustomerId());

        if (customer == null) {
            log.error("customer not exists, id: {}", addForm.getCustomerId());
            return ResponseDTO.error(UserErrorCode.CUSTOMER_NOT_EXIST);
        }

        // 检查座位是否已经被其他人占用
        List<RolandSeaEntity> orderSeats = rolandOrderDao.selectOrderSeatsBySeatId(addForm.getSeatIds());
        if (CollectionUtils.isNotEmpty(orderSeats)) {
            StringBuilder sb = new StringBuilder();
            sb.append("座位 ");
            for (RolandSeaEntity orderSeat : orderSeats) {
                sb.append(orderSeat.getSeatRow()).append("排");
                sb.append(orderSeat.getSeatNo()).append("号 ");
            }
            sb.append("已被其他人抢走，请重新选择");

            return ResponseDTO.error(UserErrorCode.SEAT_IS_OCCUPIED, sb.toString());
        }


        // 免费票数
        int freeNum = customer.getFreeNum();
        int totalNum = customer.getTotalNum();

        // 查看当前客户之前订单
        LambdaQueryWrapper<RolandOrderEntity> orderQw = Wrappers.lambdaQuery();
        orderQw.eq(RolandOrderEntity::getCustomerId, customer.getId());
        orderQw.in(RolandOrderEntity::getOrderStatus, RolandOrderEntity.STATUS_LIST);
        List<RolandOrderEntity> orderList = rolandOrderDao.selectList(orderQw);
        if (CollectionUtils.isNotEmpty(orderList)) {
            List<Integer> orderIds = orderList.stream().map(RolandOrderEntity::getId).collect(Collectors.toList());
            int seatNum = rolandOrderDao.selectSeatNum(orderIds);
            freeNum -= seatNum; // 排除掉已经使用的免费票数
            totalNum -= seatNum;
        }

        // 校验限购票数
        if (addForm.getSeatIds().size() > totalNum) {
            return ResponseDTO.error(UserErrorCode.SEAT_IS_NOT_OVERRUN, "座位超限，您总共可以选" + customer.getTotalNum() + "个座位");
        }

        // 如果已购买的票数超过了免费票数，则 freeNum 为负数，这里重置为 0
        // 例如 免费票数 = 1， 总可以购买票数为 5，已经购买的票数为 3，则 1 - 3 = -2
        freeNum = Math.max(freeNum, 0);

        // 计算票价
        BigDecimal amount = BigDecimal.ZERO;
        int payNum = addForm.getSeatIds().size() - freeNum;
        int status = 1;
        if (payNum > 0) {
            amount = new BigDecimal(payNum).multiply(price);
            status = 0;
        }

        LambdaQueryWrapper<RolandPerformanceStudentEntity> qw = Wrappers.lambdaQuery();
        qw.eq(RolandPerformanceStudentEntity::getCustomerId, addForm.getCustomerId());
        RolandPerformanceStudentEntity studentPerformance = rolandPerformanceStudentDao.selectOne(qw);

        // 异步生成电子票
        String ticket = generateTicket(customer.getId(), addForm.getPhone(), studentPerformance.getPerformanceTypeId(), addForm.getSeatIds());


        RolandOrderEntity rolandOrderEntity = new RolandOrderEntity();
        rolandOrderEntity.setCustomerId(customer.getId());
        rolandOrderEntity.setPhone(addForm.getPhone());
        rolandOrderEntity.setPerformanceTypeId(studentPerformance.getPerformanceTypeId());
        rolandOrderEntity.setOrderStatus(status);
        rolandOrderEntity.setTicket("http://47.94.90.100/ticket/" + ticket);
        rolandOrderEntity.setAmount(amount);
        rolandOrderEntity.setCreateTime(LocalDateTime.now());

        TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition());
        try {
            rolandOrderDao.insert(rolandOrderEntity);
            rolandOrderDao.saveDetail(rolandOrderEntity.getId(), addForm.getSeatIds());

            txManager.commit(txStatus);
        } catch (Exception e) {
            txManager.rollback(txStatus);
        }
        return ResponseDTO.ok(amount);
    }

    /**
     * 生成电子票据，并返回票据全路径
     *
     * @param customerId        客户 id
     * @param phone             下单时输入的手机号
     * @param performanceTypeId 下单时选择的表演曲目 id
     * @param seatIds           选择的座位 id 机会
     * @return 电子票据全路径
     */
    private String generateTicket(Integer customerId, String phone, Integer performanceTypeId, List<Integer> seatIds) {
        List<RolandSeaEntity> seatList = rolandSeaDao.selectBatchIds(seatIds);

        List<String> seatStrList = seatList.stream()
                .map(v -> v.getFloor() + "楼" + v.getSeatRow() + "排" + v.getSeatNo() + "座")
                .collect(Collectors.toList());

        String imageName = customerId + "-" + phone + "-" + performanceTypeId + "-" + UUID.fastUUID() + ".png";

        // 添加到消息队列，后面异步处理
        TICKET_QUEUE.add(new TicketGenerateDTO(seatStrList, imageName));

        return imageName;
    }


    private Thread ticketGenerateThread;
    private volatile boolean toStop = false;
    @PostConstruct
    private void generateTicket() {
        ticketGenerateThread = new Thread(() -> {
            while (!toStop) {
                try {
                    TicketGenerateDTO dto = TICKET_QUEUE.take();
                    log.info("生成电子票据，获取到要生成的数据，{}", JSON.toJSONString(dto));

                    String imgSourcePath = ticketPath + "ticket-template.jpg";
                    String imgTargetPath = ticketPath + dto.getImageName();
                    int y = -100;
                    for (String seat : dto.getSeatStrList()) {
                        ImgUtil.pressText(
                                FileUtil.file(imgSourcePath),
                                FileUtil.file(imgTargetPath),
                                seat, Color.BLACK,
                                new Font("Microsoft YaHei", Font.BOLD, 60),
                                1370,
                                y,
                                1.0f
                        );
                        // 如果多个座位，将输出图片当做输入来继续写
                        y += 70;
                        imgSourcePath = imgTargetPath;
                    }
                } catch (InterruptedException e) {
                    if (!toStop) {
                        log.error("生成电子票据，从队列中获取值失败， ", e);
                    }
                }
            }

            log.info(">>>>>>>>>>> 生成电子票据, TicketGenerateThread destroy.");
        });
        ticketGenerateThread.setDaemon(true);
        ticketGenerateThread.setName("TicketGenerateThread");
        ticketGenerateThread.start();
    }

    @PreDestroy
    private void toStop() {
        toStop = true;
        if (ticketGenerateThread != null) {    // support empty admin address
            ticketGenerateThread.interrupt();
            try {
                ticketGenerateThread.join();
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
        }
    }





    public static void main(String[] args) {
        List<String> seatStrList = Stream.of("1楼1排13座", "1楼1排14座", "1楼1排15座", "1楼1排16座", "1楼1排17座").collect(Collectors.toList());

        String imgSourcePath = "/Users/dragon/Documents/roland/ticket-template.jpg";
        String imgTargetPath = "/Users/dragon/Pictures/1-13333333333-1-" + UUID.fastUUID() + ".png";
        int y = -100;
        for (String seat : seatStrList) {
            ImgUtil.pressText(
                    FileUtil.file(imgSourcePath),
                    FileUtil.file(imgTargetPath),
                    seat, Color.BLACK,
                    new Font("Microsoft YaHei", Font.BOLD, 60),
                    1370,
                    y,
                    1.0f
            );
            // 如果多个座位，将输出图片当做输入来继续写
            System.out.println(y);
            y += 70;
            imgSourcePath = imgTargetPath;
        }
    }


    /**
     * 更新
     *
     * @param updateForm
     * @return
     */
    public ResponseDTO<String> updateStatus(RolandOrderUpdateForm updateForm) {
        LambdaUpdateWrapper<RolandOrderEntity> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(RolandOrderEntity::getId, updateForm.getId());
        updateWrapper.set(RolandOrderEntity::getOrderStatus, updateForm.getOrderStatus());
        if (updateForm.getOrderStatus() == 1) {
            updateWrapper.set(RolandOrderEntity::getPayTime, LocalDateTime.now());
        }
        rolandOrderDao.update(null, updateWrapper);

        return ResponseDTO.ok();
    }

    /**
     * 批量删除
     *
     * @param idList
     * @return
     */
    public ResponseDTO<String> batchDelete(List<Integer> idList) {
        if (CollectionUtils.isEmpty(idList)) {
            return ResponseDTO.ok();
        }

        rolandOrderDao.deleteBatchIds(idList);
        rolandOrderDao.deleteDetailByOrderIds(idList);
        return ResponseDTO.ok();
    }

    /**
     * 单个删除
     */
    public ResponseDTO<String> delete(Integer id) {
        if (null == id) {
            return ResponseDTO.ok();
        }

        rolandOrderDao.deleteById(id);
        rolandOrderDao.deleteDetailByOrderId(id);
        return ResponseDTO.ok();
    }

    public ResponseDTO<List<RolandOrderDetailVO>> detail(Integer id) {
        List<RolandOrderDetailVO> result = rolandOrderDao.selectDetail(id);
        return ResponseDTO.ok(result);
    }

    public ResponseDTO<List<RolandMyTicketVO>> ticket(RolandTicketQueryForm param) {
        List<RolandOrderVO> orderList = rolandOrderDao.selectCustomerOrders(Base64.decodeStr(param.getStudentName()), param.getPhone());
        if (CollectionUtils.isEmpty(orderList)) {
            return ResponseDTO.ok();
        }

        List<Integer> orderIds = orderList.stream().map(RolandOrderVO::getId).collect(Collectors.toList());

        Map<Integer, List<RolandOrderDetailVO>> orderDetailMap = rolandOrderDao.selectOrderDetailByOrderId(orderIds)
                .stream().collect(Collectors.groupingBy(RolandOrderDetailVO::getOrderId));

        List<RolandMyTicketVO> result = orderList.stream().map(v -> {
            List<RolandOrderDetailVO> orderDetail = orderDetailMap.get(v.getId());
            List<String> seats = new ArrayList<>();
            if (orderDetail != null) {
                seats = orderDetail.stream()
                        .map(d -> d.getFloor() + "层" + d.getSeatRow() + "排" + d.getSeatNo() + "座")
                        .collect(Collectors.toList());
            }

            return new RolandMyTicketVO(v.getOrderStatusStr(), v.getTicket(), seats);
        }).collect(Collectors.toList());

        return ResponseDTO.ok(result);
    }

    public void export(RolandOrderQueryForm queryForm, HttpServletResponse response) {
        List<RolandOrderExcelVO> orderList = rolandOrderDao.selectByParam(queryForm);

        try {
            SmartEasyPoiExcelUtil.exportExcel(orderList, null, "sheet1", RolandOrderExcelVO.class, "订单", response);
        } catch (IOException e) {
            log.error("订单导出失败, ", e);
        }
    }
}
